package common {
	import __AS3__.vec.Vector;
	
	import actors.Actor;
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.geom.Point;
	
	import interfaces.IDestroyable;
	
	public class ActorMaster implements IDestroyable {
		private static var _instance:ActorMaster;
		
		private var _actors:Vector.<Actor>;
		private var _spawners:Vector.<IDestroyable>; //keep track of them here so they can be destroyed
		private var _fishLayer:DisplayObjectContainer;
		
		private var _destroyed:Boolean;
		
		public static function get instance():ActorMaster {
			if (!_instance)
				_instance = new ActorMaster();
			
			return _instance;
		}
		
		public function ActorMaster() {
			_destroyed = false;
		}
		
		public function init(fishLayer:DisplayObjectContainer):void {
			_actors = new Vector.<Actor>();
			_spawners = new Vector.<IDestroyable>();
			_fishLayer = fishLayer;
		}
		
		public function update():void {
			for (var i:int = _actors.length - 1; i >= 0; i --) {
				var actor:Actor = _actors[i];
				if (!actor.update()) {
					_fishLayer.removeChild(actor.graphic);
					_actors.splice(i, 1);
				}
			}
		}
		
		public function addActor(actor:Actor):void {
			_actors.push(actor);
			_fishLayer.addChild(actor.graphic);
		}
		
		public function addSpawner(fishSpawner:IDestroyable):void {
			_spawners.push(fishSpawner);
		}
		
		public function removeActorGraphic(actorGraphic:DisplayObject):void {
			_fishLayer.removeChild(actorGraphic);
		}
		
		public function addActorGraphic(actorGraphic:DisplayObject):void {
			_fishLayer.addChild(actorGraphic);
		}
		
		public function findClosest(loc:Point, vector:Vector.<Actor> = null, type:Class = null):Actor {
			if (vector == null)
				vector = _actors;
			var closest:Actor;
			var dist:Number = Number.MAX_VALUE;
			for each (var actor:Actor in vector) {
				if (type == null || actor is type) {
					var actorDist:Number = Point.distance(loc, actor.loc);
					if (actorDist < dist) {
						closest = actor;
						dist = actorDist;
					}
				}
			}
			
			return closest;
		}
		
		public function findInRange(loc:Point, range:Number, vector:Vector.<Actor> = null, sort:Boolean = false, type:Class = null):Vector.<Actor> {
			if (!vector)
				vector = _actors;
				
			var inRange:Vector.<Actor> = new Vector.<Actor>();
			for each (var actor:Actor in vector) {
				if (type == null || actor is type) {
					var actorDist:Number = Point.distance(loc, actor.loc);
					if (actorDist < range)
						inRange.push(actor);
				}
			}
			
			//TODO: optimize with better sorting algorithm
			if (sort) {
				//sort vector
				var sorted:Vector.<Actor> = new Vector.<Actor>();
				while (inRange.length > 0) {
					var closest:Actor = findClosest(loc, inRange);
					for (var i:int = inRange.length - 1; i >= 0; i --) {
						if (closest === inRange[i])
							inRange.splice(i, 1);
					}
					sorted.push(closest);
				}
				inRange = sorted;
			}
			return inRange;
		}
		
		public function countType(type:Class):int {
			if (!_actors)
				return 0;
			
			var count:int = 0;
			for (var i:int = 0; i < _actors.length; i ++) {
				if (_actors[i] is type)
					count ++;
			}
			
			return count;
		}
		
		public function get destroyed():Boolean {
			return _destroyed;
		}
		
		public function destroy():void {
			if (destroyed)
				return;
			_destroyed = true;
			
			_instance = null;
			_actors = null;
			for each (var spawner:IDestroyable in _spawners) {
				spawner.destroy();
			}
			_fishLayer = null;
		}
	}
}
